package com.swak.reactivex.transport;

import static com.swak.Constants.CLIENT_SIDE;
import static com.swak.Constants.SERVER_SIDE;
import static com.swak.Constants.SIDE;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.Constants;
import com.swak.reactivex.transport.exception.CodecException;
import com.swak.reactivex.transport.exception.ExceedPayloadLimitException;
import com.swak.registry.URL;
import com.swak.spi.SPI;

import io.netty.buffer.ByteBuf;

/**
 * 编解码
 * 
 * @author 618lf
 */
@SPI("ExchangeCodec")
public interface Codec {

	public static Logger logger = LoggerFactory.getLogger(Codec.class);

	public enum DecodeResult {
		NEED_MORE_INPUT, SKIP_SOME_INPUT, CHECK_MAGIC_ERROR
	}

	public static final String Encoder_Name = "encoder";
	public static final String Decoder_Name = "decoder";
	public static final String Upgrader_Name = "upgrader";

	/**
	 * 是否是客户端
	 * 
	 * @return
	 */
	public static boolean isClientSide(Channel channel) {
		return !isServerSide(channel);
	}

	/**
	 * 是否是服务器端
	 * 
	 * @return
	 */
	public static boolean isServerSide(Channel channel) {
		String side = (String) channel.getAttribute(SIDE);
		if (CLIENT_SIDE.equals(side)) {
			return false;
		} else if (SERVER_SIDE.equals(side)) {
			return true;
		} else {
			InetSocketAddress address = channel.remoteAddress();
			URL url = channel.url();
			boolean isServer = url.getPort() == address.getPort()
					&& url.getHost().equals(address.getAddress().getHostAddress());
			channel.setAttribute(SIDE, isServer ? SERVER_SIDE : CLIENT_SIDE);
			return isServer;
		}
	}

	/**
	 * 校验载荷是否超过配置的大小
	 * 
	 * @param channel
	 * @param size
	 * @throws IOException
	 */
	public static void checkPayload(Channel channel, long size) throws IOException {
		int payload = getPayload(channel);
		boolean overPayload = isOverPayload(payload, size);
		if (overPayload) {
			ExceedPayloadLimitException e = new ExceedPayloadLimitException(
					"Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);
			logger.error("Too large data  Error {}", e);
			throw e;
		}
	}

	/**
	 * 校验载荷是否超过配置的大小
	 * 
	 * @param channel
	 * @param size
	 * @throws IOException
	 */
	public static ExceedPayloadLimitException checkPayloadQuietly(Channel channel, long size) {
		int payload = getPayload(channel);
		boolean overPayload = isOverPayload(payload, size);
		if (overPayload) {
			return new ExceedPayloadLimitException(
					"Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);
		}
		return null;
	}

	public static int getPayload(Channel channel) {
		int payload = Constants.DEFAULT_PAYLOAD;
		if (channel != null && channel.url() != null) {
			payload = channel.url().getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD);
		}
		return payload;
	}

	public static boolean isOverPayload(int payload, long size) {
		if (payload > 0 && size > payload) {
			return true;
		}
		return false;
	}

	/**
	 * 支持的数据类型
	 * 
	 * @param message
	 * @return
	 */
	default boolean support(Object message) {
		return true;
	}

	/**
	 * 编码
	 * 
	 * @param channel
	 * @param buffer
	 * @param message
	 * @throws IOException
	 */
	void encode(Channel channel, ByteBuf buffer, Object message) throws CodecException;

	/**
	 * 解码
	 * 
	 * @param channel
	 * @param buffer
	 * @return
	 * @throws IOException
	 */
	Object decode(Channel channel, ByteBuf buffer) throws CodecException;
}
